此範例針對藝文活動售票情境,每日有限制售票票數,入門前需要驗票,單日無限次入場;在合約設計方面,加入售票結束時間,保有單日最高參與者數量(限制票數),不過不限制單人買票數量,所以每天都要發布新的CONTRACT代表當天售票狀況,買票時FUNCTION會有兩個判斷機制(時間、參與者數量);再來是驗票的部分,每個人買票時都會產生一筆HASH VLAUE,假設活動現場採用QR CODE掃描驗證票券真假,就是驗證HASH VALUE有沒有在address購買的集合中
pragma solidity^0.4.25;
//以finney為單位,1 finney == 0.001 ether
contract Ticket{
address public Host; //發起人-主辦單位
uint256 public TicketPrice; //當日票價
uint256 public MaxParticipants; //當日票數
uint256 public SalesTime; //當日販售結束時間
uint256 public SalesDate; //當日販售日期
uint256 public amountOfParticipant;
mapping(address => bytes32[]) public tickets;
mapping(uint => address) public IdToUser;
constructor(uint256 _price, uint256 _participants, uint256 _time) public{
Host = msg.sender;
TicketPrice = _price*10**15;
MaxParticipants = _participants;
SalesTime = now + _time;
SalesDate = now;
}
//買票
function () external payable{
buyTikcet();
}
//買票邏輯
function buyTikcet() private{
require(now <= SalesTime,"超出時間"); //確認有無超過販售時間
require(amountOfParticipant < MaxParticipants,"今日票價已售完"); //確認沒有超過總售票量
require(msg.value == TicketPrice,"不符合票價"); //確認支付的錢跟售價一樣
amountOfParticipant++;
//插入轉換hash function
bytes32 ticketHash = keccak256(abi.encodePacked(amountOfParticipant,msg.sender)); //將userId和user address一同hash
tickets[msg.sender].push(ticketHash); //紀錄
IdToUser[amountOfParticipant] = msg.sender;
}
//驗票
function verify(bytes32 _ticketHash) public view returns(bool){
require(now <= SalesTime,"超出時間");
for(uint i = 0 ; i < tickets[msg.sender].length ; i++){
if(_ticketHash == tickets[msg.sender][i]){
return true;
}
}
}
//主辦提款
function withdraw() public{
Host.transfer(address(this).balance);
}
}
address public Host; //發起人-主辦單位
uint256 public TicketPrice; //當日票價
uint256 public MaxParticipants; //當日票數
uint256 public SalesTime; //當日販售結束時間
uint256 public SalesDate; //當日販售日期
uint256 public amountOfParticipant; //參與者數量
mapping(address => bytes32[]) public tickets; //value為byts32陣列,一個address可以買多張票,每個票都有一個tickets hash
mapping(uint => address) public IdToUser; //票券ID對應的購買者
constructor(uint256 _price, uint256 _participants, uint256 _time) public{
Host = msg.sender;
TicketPrice = _price*10**15;
MaxParticipants = _participants;
SalesTime = now + _time;
SalesDate = now;
}
發起合約depoly contract時,需要輸入當日售票票價(以finney為計價單位,1 finney
== 0.001 ether
)、當日票券總數量(以參與者數量計算)、販售時間
function () external payable{
buyTikcet();
}
function buyTikcet() private{
require(now <= SalesTime,"超出時間"); //確認有無超過販售時間
require(amountOfParticipant < MaxParticipants,"今日票價已售完"); //確認沒有超過總售票量
require(msg.value == TicketPrice,"不符合票價"); //確認支付的錢跟售價一樣
amountOfParticipant++;
//插入轉換hash function
bytes32 ticketHash = keccak256(abi.encodePacked(amountOfParticipant,msg.sender)); //將userId和user address一同hash
tickets[msg.sender].push(ticketHash); //紀錄
IdToUser[amountOfParticipant] = msg.sender;
}
fallback function
並支付發起合約時所訂的價格,一次買一張票。fallback function
為external
,代表只提供給外部call function,搭配payable
執行時需要支付一筆ether;buyTicket()
function 為private
代表只能在合約內部執行且不公開,當fallback function執行時,會執行buyTicket()
function,判斷目前有沒有超過販售時間、有沒有剩餘的票券、支付的錢有沒有符合售票價格。通過條件判斷之後,增加參與者數量,以keccak256 hash function
將 參與者數量(也可以當作票券ID) 和買票的address 共同HASH,產生出32bytes的票券hash value,作為當日驗票的證明。最後,票券hash value 會push到 mapping tickets[msg.sender]
紀錄在買票的address的bytes32 array
function verify(bytes32 _ticketHash) public view returns(bool){
for(uint i = 0 ; i < tickets[msg.sender].length ; i++){
if(_ticketHash == tickets[msg.sender][i]){
return true;
}
}
}
function withdraw() public{
Host.transfer(address(this).balance);
}
在verify()
function 輸入票券hash,for loop
會先根據mapping tickets[msg.sender]
找到目前呼叫function的address,所擁有的票券hash,如果輸入的參數符合address擁有的票券之一,回傳true